如何正确克隆 JavaScript 对象?

您所在的位置:网站首页 apple watch1大小 如何正确克隆 JavaScript 对象?

如何正确克隆 JavaScript 对象?

2024-06-19 06:26| 来源: 网络整理| 查看: 265

问:

我有一个对象 x。我想将它复制为对象 y,这样对 y 的更改就不会修改 x。我意识到复制从内置 JavaScript 对象派生的对象会导致额外的、不需要的属性。这不是问题,因为我正在复制我自己的文字构造对象之一。

如何正确克隆 JavaScript 对象?

答1:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

2022 年更新

有一个名为 structured cloning 的新 JS 标准。它适用于所有浏览器:

const clone = structuredClone(object);

旧答案

对 JavaScript 中的任何对象执行此操作都不会简单或直接。您将遇到错误地从对象原型中获取属性的问题,这些属性应该留在原型中而不是复制到新实例中。例如,如果您将 clone 方法添加到 Object.prototype,正如一些答案所描述的,您将需要明确跳过该属性。但是,如果在 Object.prototype 中添加了其他附加方法,或者其他中间原型,您不知道怎么办?在这种情况下,您将复制不应复制的属性,因此您需要使用 hasOwnProperty 方法检测不可预见的非本地属性。

除了不可枚举的属性之外,当您尝试复制具有隐藏属性的对象时,您还会遇到更棘手的问题。例如,prototype 是函数的隐藏属性。此外,对象的原型由属性 proto 引用,该属性也是隐藏的,不会被迭代源对象属性的 for/in 循环复制。我认为 proto 可能特定于 Firefox 的 JavaScript 解释器,并且在其他浏览器中可能有所不同,但你明白了。并非所有事物都是可枚举的。如果您知道它的名称,您可以复制隐藏的属性,但我不知道有什么方法可以自动发现它。

寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。如果您的源对象的原型是 Object,那么只需使用 {} 创建一个新的通用对象即可,但如果源的原型是 Object 的某个后代,那么您将丢失该原型的其他成员您使用 hasOwnProperty 过滤器跳过了哪些,或者哪些在原型中,但一开始就无法枚举。一种解决方案可能是调用源对象的 constructor 属性来获取初始复制对象,然后复制属性,但是您仍然不会获得不可枚举的属性。例如,Date 对象将其数据存储为隐藏成员:

function clone(obj) { if (null == obj || "object" != typeof obj) return obj; var copy = obj.constructor(); for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; } return copy; } var d1 = new Date(); /* Executes function after 5 seconds. */ setTimeout(function(){ var d2 = clone(d1); alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString()); }, 5000);

d1 的日期字符串将比 d2 晚 5 秒。使一个 Date 与另一个相同的方法是调用 setTime 方法,但这是特定于 Date 类的。我认为这个问题没有万无一失的通用解决方案,尽管我很乐意犯错!

当我不得不实现一般的深度复制时,我最终妥协,假设我只需要复制一个普通的 Object、Array、Date、String、Number 或 Boolean。最后 3 种类型是不可变的,所以我可以执行浅拷贝而不用担心它会改变。我进一步假设 Object 或 Array 中包含的任何元素也将是该列表中的 6 个简单类型之一。这可以通过如下代码来完成:

function clone(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i Object { x: "b", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } }

…你看,它不适用于内部的嵌套结构。

原生解决方案的 polyfill

与 IE 8 一样,旧版浏览器中有一个用于 Object.create 的 polyfill。它类似于 Mozilla 推荐的东西,当然,它并不完美,并且会导致与 本机解决方案相同的问题。

function F() {}; function clonePF(o) { F.prototype = o; return new F(); } var b = clonePF(a); b.x = 'b'; b.nested.y = 'b';

我已将 F 放在范围之外,因此我们可以看看 instanceof 告诉我们什么。

console.log(a, b); a --> Object { x: "a", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } b --> F { x: "b", circ: Circ { me: Circ { ... } }, nested: Nested { y: "b" } } console.log(typeof a, typeof b); a --> object b --> object console.log(a instanceof Object, b instanceof Object); a --> true b --> true console.log(a instanceof F, b instanceof F); a --> false b --> true

与本机解决方案相同的问题,但输出稍差一些。

更好(但不完美)的解决方案

在四处挖掘时,我发现了一个与这个问题类似的问题 (In Javascript, when performing a deep copy, how do I avoid a cycle, due to a property being “this”?),但有一个更好的解决方案。

function cloneDR(o) { const gdcc = "__getDeepCircularCopy__"; if (o !== Object(o)) { return o; // primitive value } var set = gdcc in o, cache = o[gdcc], result; if (set && typeof cache == "function") { return cache(); } // else o[gdcc] = function() { return result; }; // overwrite if (o instanceof Array) { result = []; for (var i=0; i Object { x: "a", circ: Object { me: Object { ... } }, nested: Object { y: "a" } } b --> Object { x: "b", circ: Object { me: Object { ... } }, nested: Object { y: "b" } } console.log(typeof a, typeof b); a --> object b --> object console.log(a instanceof Object, b instanceof Object); a --> true b --> true console.log(a instanceof F, b instanceof F); a --> false b --> false

要求是匹配的,但还有一些小问题,包括将 nested 的 instance 和 circ 更改为 Object。

共享叶子的树的结构不会被复制,它们将成为两个独立的叶子:

[Object] [Object] / \ / \ / \ / \ |/_ _\| |/_ _\| [Object] [Object] ===> [Object] [Object] \ / | | \ / | | _\| |/_ \|/ \|/ [Object] [Object] [Object]

结论

最后一个使用递归和缓存的解决方案可能不是最好的,但它是对象的真实深层副本。它处理简单的 properties、circular structures 和 nested object,但在克隆时会弄乱它们的实例。

jsfiddle

所以结论是避免这个问题:)

@mikus 直到有一个真正的规范,它不仅涵盖基本用例,是的。

对上面提供的解决方案进行了很好的分析,但作者得出的结论表明这个问题没有解决方案。

遗憾的是 JS 不包含原生克隆功能。

在所有最佳答案中,我觉得这接近正确的答案。

答9:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

如果您对浅拷贝没问题,underscore.js 库有一个 clone 方法。

y = _.clone(x);

或者你可以像这样扩展它

copiedObject = _.extend({},originalObject);

谢谢。在 Meteor 服务器上使用此技术。

要快速开始使用 lodash,我建议学习 npm、Browserify 以及 lodash。我让克隆与 'npm i --save lodash.clone' 一起工作,然后是 'var clone = require('lodash.clone');'要让 require 工作,你需要类似 browserify 的东西。安装并了解它的工作原理后,您将在每次运行代码时使用“browserify yourfile.js > bundle.js;start chrome index.html”(而不是直接进入 Chrome)。这会将您的文件和 npm 模块所需的所有文件收集到 bundle.js 中。不过,您可能可以使用 Gulp 节省时间并自动执行此步骤。

答10:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

好的,假设你在下面有这个对象并且你想克隆它:

let obj = {a:1, b:2, c:3}; //ES6

或者

var obj = {a:1, b:2, c:3}; //ES5

答案主要取决于您使用的 ECMAscript,在 ES6+ 中,您可以简单地使用 Object.assign 进行克隆:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

或像这样使用扩展运算符:

let cloned = {...obj}; //new {a:1, b:2, c:3};

但是如果你使用 ES5,你可以使用几个方法,但是 JSON.stringify,只要确保你不使用大量数据来复制,但在很多情况下它可能是一个方便的方式,像这样:

let cloned = JSON.parse(JSON.stringify(obj)); //new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

您能否举例说明 big chunk of data 的含义? 100KB? 100MB?谢谢!

是的,@ user1063287,基本上更大的数据,性能更差......所以这真的取决于,而不是kb,mb或gb,更多的是你想要做多少次......而且它也行不通对于功能和其他东西......

Object.assign 制作浅拷贝(就像传播一样,@Alizera)

你不能在 es5 中使用 let :^) @Alireza

答11:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

2020 年 7 月 6 日更新

有三 (3) 种方法可以在 JavaScript 中克隆对象。由于 JavaScript 中的对象是引用值,因此您不能简单地使用 = 进行复制。

方法是:

const food = { food: 'apple', drink: 'milk' } // 1. Using the "Spread" // ------------------ { ...food } // 2. Using "Object.assign" // ------------------ Object.assign({}, food) // 3. "JSON" // ------------------ JSON.parse(JSON.stringify(food)) // RESULT: // { food: 'apple', drink: 'milk' }

这可以作为参考总结。

这为这个问题增加了哪些新的/独特的信息?

JSON 方法将删除对象的任何方法

从一个对象创建一个字符串,然后将该字符串解析为另一个对象只是为了复制该对象是一种 Monty Python 的编程风格:-D

这仅适用于对象字面量和可以这样表示的对象,但不适用于您在 OO 语言中遇到的通用“对象”。这似乎是 OP 要求的,因此没关系,但它不是适用于每种对象的通用解决方案。

对于具有层次结构的对象,扩展运算符和 Object.assign 失败,即。嵌套对象。 JSON.parse/stringify 有效,但如上所述不复制方法。

原文链接:https://www.huntsbot.com/qa/rDo0/how-do-i-correctly-clone-a-javascript-object?lang=zh_CN&from=csdn

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3